using System;
using DG.Tweening;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

namespace DoTweenExtends
{
    [Serializable]
    public class SequenceItem
    {
        [Title("Base")] public TweenType type;

        [ShowIf("ShouldTransform", false)] [LabelText("Target")]
        public Transform transformTarget;

        [ShowIf("ShouldVector3Enum", false), EnumToggleButtons, HideLabel]
        public Vector3Type vector3Type;

        [ShowIf("ShouldVector2Enum", false), EnumToggleButtons, HideLabel]
        public Vector2Type vector2Type;

        [ShowIf("ShouldEffectType", false), EnumToggleButtons, HideLabel]
        public EffectType effectType = EffectType.Position;

        [ShowIf("ShouldFadeType", false), EnumToggleButtons, HideLabel]
        public FadeType fadeType;

        [ShowIf("ShouldCanvasGroup", false)] [LabelText("Target")]
        public CanvasGroup canvasGroupTarget;

        [ShowIf("ShouldGraphic", false)] [LabelText("Target")]
        public Graphic graphicTarget;

        [ShowIf("ShouldValue", false)] [LabelText("To")]
        public float toValue;

        [ShowIf("ShouldVector3", false)] [LabelText("To")]
        public Vector3 toVector3;

        [ShowIf("ShouldVector2", false)] [LabelText("To")]
        public Vector2 toVector2;

        [ShowIf("ShouldColor", false)] [LabelText("To")]
        public Color toColor = Color.white;

        public bool isFrom;
        [ShowIf("ShouldTransform", false)] public bool relative;

        [ShowIf("ShouldRotateMode", false)] public RotateMode rotateMode = RotateMode.Fast;

        [ShowIf("ShouldEffectType", false)] [Range(1, 50)]
        public int vibrato = 10; // shake和punch的颤动次数

        [ShowIf("type", TweenType.Shake)] [Range(0, 90)]
        public float randomness = 90f; //shake的随机度

        [ShowIf("type", TweenType.Shake)] [Range(1, 100)]
        public float strength = 10; // shake的强度

        [ShowIf("type", TweenType.Shake)] public ShakeRandomnessMode shakeRandomness;

        [ShowIf("type", TweenType.Punch)] [Range(0, 1)]
        public float elasticity = 1f; // push的弹性系数

        public float duration = 1f;
        public bool speedBased;
        public float delay;

        public int loops = 1;

        [ShowIf("ShouldLoopType", false)] public LoopType loopType = LoopType.Restart;

        public Ease easeType = Ease.OutBack;

        [ShowIf("easeType", Ease.INTERNAL_Custom)]
        public AnimationCurve animationCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);


        [Title("Events")] public EventType eventTypes;

        [ShowIf("ShouldCompleteEvent")] public UnityEvent onComplete;

        [ShowIf("ShouldStartEvent")] public UnityEvent onStart;

        [ShowIf("ShouldPlayEvent")] public UnityEvent onPlay;

        [ShowIf("ShouldPauseEvent")] public UnityEvent onPause;

        [ShowIf("ShouldRewindEvent")] public UnityEvent onRewind;

        [ShowIf("ShouldUpdateEvent")] public UnityEvent onUpdate;

        [ShowIf("ShouldKillEvent")] public UnityEvent onKill;


        public Tween CreateTween()
        {
            Tweener tween;

            switch (type)
            {
                case TweenType.Move:
                    if (transformTarget == null)
                        throw new ArgumentNullException(nameof(transformTarget));

                    tween = vector3Type switch
                    {
                        Vector3Type.All => transformTarget.DOMove(toVector3, duration),
                        Vector3Type.X => transformTarget.DOMoveX(toValue, duration),
                        Vector3Type.Y => transformTarget.DOMoveY(toValue, duration),
                        Vector3Type.Z => transformTarget.DOMoveZ(toValue, duration),
                        _ => throw new ArgumentOutOfRangeException()
                    };

                    break;

                case TweenType.LocalMove:
                    if (transformTarget == null)
                        throw new ArgumentNullException(nameof(transformTarget));

                    tween = vector3Type switch
                    {
                        Vector3Type.All => transformTarget.DOLocalMove(toVector3, duration),
                        Vector3Type.X => transformTarget.DOLocalMoveX(toValue, duration),
                        Vector3Type.Y => transformTarget.DOLocalMoveY(toValue, duration),
                        Vector3Type.Z => transformTarget.DOLocalMoveZ(toValue, duration),
                        _ => throw new ArgumentOutOfRangeException()
                    };

                    break;

                case TweenType.Scale:
                    if (transformTarget == null)
                        throw new ArgumentNullException(nameof(transformTarget));

                    tween = vector3Type switch
                    {
                        Vector3Type.All => transformTarget.DOScale(toVector3, duration),
                        Vector3Type.X => transformTarget.DOScaleX(toValue, duration),
                        Vector3Type.Y => transformTarget.DOScaleY(toValue, duration),
                        Vector3Type.Z => transformTarget.DOScaleZ(toValue, duration),
                        _ => throw new ArgumentOutOfRangeException()
                    };

                    break;

                case TweenType.Rotate:
                    if (transformTarget == null)
                        throw new ArgumentNullException(nameof(transformTarget));
                    tween = transformTarget.DORotate(toVector3, duration, rotateMode);

                    break;

                case TweenType.LocalRotate:
                    if (transformTarget == null)
                        throw new ArgumentNullException(nameof(transformTarget));
                    tween = transformTarget.DOLocalRotate(toVector3, duration, rotateMode);
                    break;

                case TweenType.Fade:
                    switch (fadeType)
                    {
                        case FadeType.CanvasGroup:
                        {
                            if (canvasGroupTarget == null)
                                throw new ArgumentNullException(nameof(canvasGroupTarget));

                            tween = canvasGroupTarget.DOFade(toValue, duration);
                            break;
                        }
                        case FadeType.Graphic:
                        {
                            if (graphicTarget == null)
                                throw new ArgumentNullException(nameof(graphicTarget));

                            tween = graphicTarget.DOFade(toValue, duration);
                            break;
                        }
                        default:
                            throw new ArgumentOutOfRangeException();
                    }

                    break;

                case TweenType.Color:
                    if (graphicTarget == null)
                        throw new ArgumentNullException(nameof(graphicTarget));

                    tween = graphicTarget.DOColor(toColor, duration);
                    break;

                case TweenType.AnchorPos:
                    if (transformTarget == null)
                        throw new ArgumentNullException(nameof(transformTarget));

                    if (transformTarget is RectTransform rect)
                    {
                        tween = vector2Type switch
                        {
                            Vector2Type.All => rect.DOAnchorPos(toVector2, duration),
                            Vector2Type.X => rect.DOAnchorPosX(toValue, duration),
                            Vector2Type.Y => rect.DOAnchorPosY(toValue, duration),
                            _ => throw new ArgumentOutOfRangeException()
                        };
                    }
                    else
                    {
                        throw new InvalidOperationException();
                    }

                    break;
                case TweenType.Punch:
                    switch (effectType)
                    {
                        case EffectType.Position:
                            tween = transformTarget.DOPunchPosition(toVector3, duration, vibrato, elasticity);
                            break;
                        case EffectType.Rotation:
                            tween = transformTarget.DOPunchRotation(toVector3, duration, vibrato, elasticity);
                            break;
                        case EffectType.Scale:
                            tween = transformTarget.DOPunchScale(toVector3, duration, vibrato, elasticity);
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }

                    break;
                case TweenType.Shake:

                    switch (effectType)
                    {
                        case EffectType.Position:
                            tween = transformTarget.DOShakePosition(duration, strength, vibrato, randomness, false, true, shakeRandomness);
                            break;
                        case EffectType.Rotation:
                            tween = transformTarget.DOShakeRotation(duration, strength, vibrato, randomness, true, shakeRandomness);
                            break;
                        case EffectType.Scale:
                            tween = transformTarget.DOShakeScale(duration, strength, vibrato, randomness, true, shakeRandomness);
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }

                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            if (tween == null)
                return null;

            if (speedBased)
                tween.SetSpeedBased(true);

            if (isFrom)
                tween.From();
            if (relative)
                tween.SetRelative();
            if (easeType == Ease.INTERNAL_Custom)
                tween.SetEase(animationCurve);
            else
                tween.SetEase(easeType);

            tween.SetDelay(delay);
            tween.SetLoops(loops < 0 ? int.MaxValue : loops, loopType);

            tween.OnStart(onStart.Invoke);
            tween.OnPlay(onPlay.Invoke);
            tween.OnPause(onPause.Invoke);
            tween.OnRewind(onRewind.Invoke);
            tween.OnUpdate(onUpdate.Invoke);
            tween.OnComplete(onComplete.Invoke);
            tween.OnKill(onKill.Invoke);

            return tween;
        }


        #region 编辑器辅助

        /*[Button("Speed Based", ButtonSizes.Small), GUIColor(1f, 0.8f, 0)]
        [ShowIf("speedBased", false)]
        private void OpenSpeedBased()
        {
            speedBased = false;
        }

        [Button("Speed Based", ButtonSizes.Small)]
        [HideIf("speedBased", false)]
        private void CloseSpeedBased()
        {
            speedBased = true;
        }*/


        private bool ShouldCompleteEvent()
        {
            return eventTypes.HasFlag(EventType.OnComplete);
        }

        private bool ShouldStartEvent()
        {
            return eventTypes.HasFlag(EventType.OnStart);
        }

        private bool ShouldPlayEvent()
        {
            return eventTypes.HasFlag(EventType.OnPlay);
        }

        private bool ShouldPauseEvent()
        {
            return eventTypes.HasFlag(EventType.OnPause);
        }

        private bool ShouldRewindEvent()
        {
            return eventTypes.HasFlag(EventType.OnRewind);
        }

        private bool ShouldUpdateEvent()
        {
            return eventTypes.HasFlag(EventType.OnUpdate);
        }

        private bool ShouldKillEvent()
        {
            return eventTypes.HasFlag(EventType.OnKill);
        }

        private bool ShouldTransform()
        {
            return type == TweenType.Move || type == TweenType.LocalMove || type == TweenType.Scale || type == TweenType.Rotate || type == TweenType.LocalRotate || type == TweenType.AnchorPos ||
                   type == TweenType.Shake || type == TweenType.Punch;
        }

        private bool ShouldGraphic()
        {
            return (type == TweenType.Fade && fadeType == FadeType.Graphic) || type == TweenType.Color;
        }

        private bool ShouldCanvasGroup()
        {
            return type == TweenType.Fade && fadeType == FadeType.CanvasGroup;
        }

        private bool ShouldEffectType()
        {
            return type == TweenType.Punch || type == TweenType.Shake;
        }

        private bool ShouldVector3Enum()
        {
            return type != TweenType.Color &&
                   type != TweenType.Fade &&
                   type != TweenType.Punch &&
                   type != TweenType.Shake &&
                   type != TweenType.AnchorPos &&
                   type != TweenType.Rotate &&
                   type != TweenType.LocalRotate;
        }

        private bool ShouldVector2Enum()
        {
            return type == TweenType.AnchorPos;
        }

        private bool ShouldFadeType()
        {
            return type == TweenType.Fade;
        }

        private bool ShouldColor()
        {
            return type == TweenType.Color;
        }

        private bool ShouldVector2()
        {
            return type == TweenType.AnchorPos && vector2Type == Vector2Type.All;
        }

        private bool ShouldVector3()
        {
            bool b = ((type is TweenType.Move or TweenType.LocalMove || type == TweenType.Scale) && vector3Type == Vector3Type.All) ||
                     type == TweenType.Rotate ||
                     type == TweenType.LocalRotate ||
                     type == TweenType.Shake ||
                     type == TweenType.Punch;
            return b;
        }

        private bool ShouldValue()
        {
            bool fade = type == TweenType.Fade;
            bool vector3 = (type == TweenType.Move || type == TweenType.LocalMove || type == TweenType.Scale) && vector3Type != Vector3Type.All;
            bool vector2 = type == TweenType.AnchorPos && vector2Type != Vector2Type.All;
            return fade || vector3 || vector2;
        }

        private bool ShouldLoopType()
        {
            return !(loops == 1 || loops == 0);
        }

        private bool ShouldRotateMode()
        {
            return type == TweenType.Rotate || type == TweenType.LocalRotate;
        }

        #endregion
    }
}